﻿/**
* 
* Copyright (c) 2012 Anders Høst Kjærgaard | ahkj@itu.dk
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, 
* including without limitation the rights to use, copy, modify, merge, publish, distribute, 
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial 
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/


using System;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Flatland.GeneticProgramming.CoreTypes;

namespace Flatland.GeneticProgramming.SyntaxTree {
    [Serializable]
    public class Leaf : Tree {

        private Leaf() { }

        /// <summary>
        /// Get or set the Terminsl
        /// </summary>
        public Terminal Terminal { get; set; }

        public Leaf(Terminal terminal) {
            Terminal = terminal;
        }

        /// <summary>
        /// Execute the terminal held by this leaf
        /// </summary>
        /// <param name="genoType"></param>
        /// <returns></returns>
        public override Object Execute(GenoType genoType) {
            switch (Terminal.SetType) {
                case SetType.ZERO_ARITY_FUNCTION:
                    return genoType.GetType().InvokeMember(Terminal.MethodInfo.Name, PrimitiveSet.BasicFlags | BindingFlags.InvokeMethod, null, genoType, new object[] { });

                case SetType.ARRAY_INDEX: {
                        Array arr = (Array)genoType.GetType().GetField((Terminal).FieldInfo.Name).GetValue(genoType);
                        var enumerator = arr.GetEnumerator();
                        int i = 0;
                        while (enumerator.MoveNext()) {
                            if (i == (Terminal).Index) {
                                return enumerator.Current;
                            }
                            ++i;
                        }
                        Debug.Assert(false, "Array index was not inside array: i: {0} Index: {1}" + i + "" + (Terminal).Index);
                        break;
                    }

                case SetType.CONSTANT:
                    return (Terminal).Constant;

                case SetType.VARIABLE:
                    return genoType.GetType().GetField((Terminal).FieldInfo.Name).GetValue(genoType);
            }
            return default(Object);
        }

        public override object Clone() {
            int d = Depth();
            Debug.Assert(Parent != null || 1 == Depth(), "Parent Node was null while cloning leaf that is not root");
            Debug.Assert(Terminal != null, "Function was null in leaf while cloning");
            return new Leaf(Terminal);
        }

        public override bool Equals(object obj) {
            if (obj == null) return false;
            if (obj.GetType() == typeof(Node))
                return Terminal.Equals(((Node)obj).Function);
            if(obj.GetType() == typeof(Leaf))
                return Terminal.Equals(((Leaf)obj).Terminal);
            return false;
        }

        /// <summary>
        /// True if the leaf holds a command terminal
        /// </summary>
        /// <returns></returns>
        public override bool IsCommandSet() {
            return Terminal.IsCommandSet();
        }

        public override string ToString() {
            StringBuilder str = new StringBuilder();
            str.Append("Leaf ");
            if (this.Terminal.SetType == SetType.ZERO_ARITY_FUNCTION)
                str.Append(Terminal.MethodInfo.ToString());
            else if (Terminal.SetType == SetType.ARRAY_INDEX)
                str.Append(SetType.ARRAY_INDEX).Append(": ").Append((Terminal).Index);
            else if (Terminal.SetType == SetType.VARIABLE)
                str.Append(SetType.VARIABLE).Append(": ").Append((Terminal).VarName);
            else str.Append(" Unknown terminal");
            return str.Append(" HashCode: " + GetHashCode()).ToString();
        }
    }
}